/* Scraper.cpp
Copyright (c) 2008 Michael Zahniser
Please note the license terms (MIT license) at the end of this document.

Function definitions for the Scraper class.
*/

#include "Scraper.h"

#include <sstream>

using std::ostringstream;



// Constructor:
Scraper::Scraper()
	: currentIndex(0), imageType("gif")
{
}



// Clone:
// Make a copy of this Scraper.
Scraper *Scraper::Clone() const
{
	Scraper *scraper = DoClone();
	scraper->currentIndex = currentIndex;
	scraper->imageType = imageType;
	
	return scraper;
}



// Previous:
// Navigate to the previous date for this comic.
bool Scraper::Previous(string &buffer, Curl &curl)
{
	return LoadIndex(currentIndex - 1, buffer, curl);
}



// Next:
// Navigate to the next date for this comic.
bool Scraper::Next(string &buffer, Curl &curl)
{
	return LoadIndex(currentIndex + 1, buffer, curl);
}



// Last:
// Navigate to the last date for this comic.
bool Scraper::Last(string &buffer, Curl &curl)
{
	return LoadIndex(GetLastIndex(), buffer, curl);
}



// Reload:
// Reload the image for this comic, for the current date.
bool Scraper::Reload(string &buffer, Curl &curl)
{
	return LoadIndex(currentIndex, buffer, curl);
}



// Description:
// Get the description of the current item.
string Scraper::Description() const
{
	return GetDescription(currentIndex);
}



// Load:
// Read this Scraper's state from the given input file.
void Scraper::Load(istream &in)
{
	int oldIndex = currentIndex;
	in >> currentIndex;
	if(!in)
		currentIndex = oldIndex;
}



// Save:
// Write this Scraper's state to the given output file.
void Scraper::Save(ostream &out) const
{
	out << currentIndex << ' ';
}



// SetIndex:
// This function is for derived classes to report their initial indices.
void Scraper::SetIndex(int index)
{
	currentIndex = index;
}



// DateToString:
// Convert a time into a string.
string Scraper::DateToString(const tm &date)
{
	ostringstream out;
	out << (date.tm_mon + 1) << "/" << (date.tm_mday) << "/" << (1900 + date.tm_year);
	
	return out.str();
}



// LoadIndex:
// Load the comic for the given index, based on the path given by GetPath().
// Return false if the image could not be found. If the image is loaded, set the
// current index to its index.
bool Scraper::LoadIndex(int index, string &buffer, Curl &curl)
{
	string path = GetPath(index);
	Fetch(path, buffer, curl);
	
	// If the returned data was not an image (e.g. a 404 error html page), 
	// maybe the image for today is a jpg.
	// Only try changing the path to ".jpg" if the current path ends in ".gif".
	// This was causing bugs when the path was an ASP request not ending in an extension.
	if(!buffer.empty() && IsNotAnImage(buffer) && path.substr(path.length() - 4, 4) == ".gif")
	{
		path = path.substr(0, path.length() - 4) + ".jpg";
		Fetch(path, buffer, curl);
	}

	// If an error occurred, if no data was read, or if a "404 - Not found" page
	// was returned, return false - loading the image was not successful.
	if(buffer.empty() || IsNotAnImage(buffer))
		return false;
	
	// Store the index of this comic.
	currentIndex = index;
	
	return true;
}



// Fetch:
// Fetch the contents of the given path into the given buffer.
// Returns the result code from the CURL operation.
void Scraper::Fetch(const string &path, string &buffer, Curl &curl)
{
	curl.Reset();
	if(!referer.empty())
		curl.AddHeader("Referer", referer);
	
	curl.Get(path, buffer);
	
	string apparentType = path.substr(path.length() - 3, 3);
	if(apparentType == "jpg" || apparentType == "gif" || apparentType == "png")
		imageType = apparentType;
}



// Set the referer website.
void Scraper::SetReferer(const string &referer) const
{
	this->referer = referer;
}



// IsNotAnImage:
// Perform some of the more obvious checks to see if image download failed.
bool Scraper::IsNotAnImage(const string &buffer)
{
	return (buffer.size() < 100 || buffer[0] == '<');
}



/* Copyright (c) 2008 Michael Zahniser

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE. */
